import {
  type Component,
  type ParentProps,
  createEffect,
  createMemo,
  createRenderEffect,
  createSignal,
  mergeProps,
  on,
  splitProps,
} from "solid-js";
import TextBox, { type TextBoxDefaultSlotProps, type TextBoxProps } from "../TextBox";
import { type NamedSlotTemplates, Slot, type Slots } from "@lib/Slot";
import { computePosition } from "@floating-ui/dom";
import "./index.scss";
import ListBox, {
  ListBoxItem,
  type ListBoxItemProps,
  type ListBoxProps,
} from "@lib/ListBox";
import type { JSX } from "solid-js/jsx-runtime";

export type Option = any;

export interface ComboBoxItemProps<TOption> extends ListBoxItemProps<TOption> {
  // onClick?: (option: Option, index: number) => void;
}

export interface ComboBoxSlots<TOption> extends NamedSlotTemplates {
  list?: Component<any>;
  item?: Component<ComboBoxItemProps<TOption>>;
}

export interface ComboBoxProps<TOption, TValue>
  extends Omit<
    TextBoxProps<string>,
    "type" | "slots" | "readonly" | "onClick" | "ref" | "value" | "onChange"
  > {
  options?: TOption[];
  searchable?: boolean;
  multiple?: boolean;
  disable?: (option: TOption, index: number) => boolean;
  value?: TValue | TValue[];
  onChange?: (value: TValue | TValue[]) => void;
  optionToValue?: (option: TOption) => TValue;
  optionToDisplay?: (option: TOption) => string;
}

const ComboBoxDefaultProps: Partial<ComboBoxProps<any, any>> = {
  searchable: false,
  multiple: false,
  clearable: true,
};

const ComboBox = <TOption extends {}, TValue>(
  p: ComboBoxProps<TOption, TValue> & Slots<ComboBoxSlots<TOption>>
) => {
  const [local, props] = splitProps(mergeProps(ComboBoxDefaultProps, p), [
    "slots",
    "searchable",
    "options",
    "onChange",
    "value",
    "onBlur",
    "disable",
  ]);

  let _ref!: HTMLDivElement;
  let _listRef!: HTMLDivElement;

  const getOptionValue = (option: Option) => {
    if (props.optionToValue) {
      return props.optionToValue(option) as TValue;
    } else {
      return option as TValue;
    }
  };

  const getOptionDisplay = (option: Option) => {
    if (option === undefined) {
      return "";
    }

    if (props.optionToDisplay) {
      return props.optionToDisplay(option) as string;
    } else {
      return option as string;
    }
  };

  const [show, setShow] = createSignal(false);
  const [po, setPo] = createSignal<{ left?: string; top?: string }>({});

  const keys = createMemo(() => {
    return local.options?.reduce((p, c, i) => {
      const key = getOptionValue(c);
      p[key] = c;
      return p;
    }, {});
  });

  const [_selected, _setSelected] = createSignal<{ [key: string]: Option }>({});

  const init = () => {
    if (local.value === undefined || local.value === null) {
      return;
    }

    const _init = (
      Array.isArray(local.value) ? local.value : ([local.value] as TValue[])
    )
      .filter((v) => v !== undefined && v != null)
      .reduce((p, c, i) => {
        p[`${c}`] = undefined;
        return p;
      }, {} as { [key: string]: TOption });

    local.options?.forEach((op) => {
      const v = getOptionValue(op);
      if (Object.keys(_init).includes(`${v}`)) {
        _init[`${v}`] = op;
      }
    });

    // console.log("init", local.options, _init, local.value);

    _setSelected(_init);
  };

  createRenderEffect(() => init());
  // init();

  const cp = () => {
    computePosition(_ref, _listRef, { placement: "bottom" }).then(
      ({ x, y }) => {
        setPo({
          left: `${x}px`,
          top: `${y}px`,
        });
      }
    );
  };

  const handleClick: TextBoxProps<string>["onClick"] = () => {
    // open list
    setShow(true);
  };

  const _selectedOptions = createMemo(() => {
    // local.options?.filter((o) =>
    //   props.multiple
    //     ? (local.value as TValue[])?.includes(getOptionValue(o))
    //     : (local.value as TValue) === getOptionValue(o)
    // );

    const a = _selected();
    return Object.keys(a).map((k) => a[k]) as TOption[];

    // if (local.value === undefined || local.value === null) {
    //   return;
    // }

    // const _init = (
    //   Array.isArray(local.value) ? local.value : ([local.value] as TValue[])
    // )
    //   .filter((v) => v !== undefined && v != null)
    //   .reduce((p, c, i) => {
    //     p[`${c}`] = undefined;
    //     return p;
    //   }, {} as { [key: string]: TOption });

    // return local.options?.filter((op) => {
    //   const v = getOptionValue(op);
    //   return Object.keys(_init).includes(`${v}`);
    // });
  });

  createEffect(on(_selectedOptions, (v) => console.log("_selectedOptions", v)));

  createEffect(
    on(show, (v) => {
      // console.log("show", v);
      _listRef.style.display = v ? "block" : "none";
    })
  );

  const handleItemClicked: ListBoxProps<any>["onClick"] = (option, index) => {
    if (isActive(option, index)) {
      removeSelectedItem(option);
    } else {
      addSelectedItem(option);
    }
  };

  const isActive = (option: any, index: number) => {
    const value = getOptionValue(option);
    const active =
      Object.keys(_selected()).filter((k) => k === `${value}`).length > 0;

    // console.log("check isActive", _selected(), value, option, active);

    return active;
  };

  const isDisabled = (option: any, index: number) => {
    return local.disable?.(option, index);
  };

  let _containerRef!: HTMLDivElement;

  const clearValue = () => {
    _setSelected({});

    if (props.multiple) {
      local.onChange?.([]);
    } else {
      local.onChange?.(null as any);
    }
  };

  const hasValue = createMemo(() => {
    return Object.keys(_selected()).length > 0;
  });

  const removeSelectedItem = (option: TOption) => {
    const value = getOptionValue(option);

    _setSelected((curr) => {
      return Object.keys(curr)
        .filter((t) => t !== `${value}`)
        .reduce((p, c, i) => {
          p[c] = curr[c];
          return p;
        }, {} as { [key: string]: Option });
    });

    // console.log("remove item", option, _selected());

    emitChange();
  };

  // createEffect(
  //   on(_selected, (v) => {

  //   })
  // );

  const emitChange = () => {
    const v = _selected();

    if (props.multiple) {
      const latestValues = Object.values(v).map((o) => getOptionValue(o));
      local.onChange?.(latestValues);
    } else {
      const key = Object.keys(v)[0];
      if (key) {
        local.onChange?.(getOptionValue(v[key]));
      } else {
        local.onChange?.(null as TValue);
      }
    }
  };

  const addSelectedItem = (option: TOption) => {
    const value = getOptionValue(option);

    if (props.multiple) {
      // 多选
      _setSelected((p) => {
        return {
          ...p,
          [`${value}`]: option,
        };
      });
    } else {
      _setSelected({});
      // 单选
      _setSelected({
        [`${value}`]: option,
      });
    }

    emitChange();
  };

  const [_searchText, _setSearchText] = createSignal("");
  const handleSearchTextKeyDown: JSX.HTMLAttributes<HTMLElement>["onKeyDown"] =
    (e) => {
      // Enter = 搜索
      // Escape = 取消搜索
    };

  return (
    <div ref={_containerRef}>
      {/* <div>{display()}</div> */}
      <TextBox
        ref={_ref}
        {...props}
        placeholder={hasValue() ? "" : props.placeholder}
        classList={{ comboBox: true }}
        readonly={!local.searchable}
        onClick={handleClick}
        onBlur={() => {
          setShow(false);
          local.onBlur?.();
        }}
        slots={{
          default: (ctx: TextBoxDefaultSlotProps<TValue[]>) => (
            <TextBox.ComboInput
              {...ctx}
              slots={{
                display: (_) => (
                  <TextBox.ComboInput.Display
                    slots={{
                      default: (_CTX) => getOptionDisplay(_CTX.option),
                    }}
                    value={_selectedOptions()}
                    onRemove={(v, i) => {
                      // console.log("clicked", v);
                      removeSelectedItem(v);
                    }}
                  ></TextBox.ComboInput.Display>
                ),
              }}
            ></TextBox.ComboInput>
          ),
          clear: (ctx) => (
            <TextBox.ClearButton
              onClick={clearValue}
              showClear={props.clearable && hasValue()}
            >
              清空
            </TextBox.ClearButton>
          ),
        }}
      ></TextBox>
      <div
        ref={_listRef}
        classList={{ "comboBox-list": true }}
        style={{
          left: po().left,
          top: po().top,
        }}
        onMouseDown={(e) => e.preventDefault()}
      >
        <ListBox
          datas={local.options}
          // onClick={handleItemClicked}
          slots={{
            item: (ctx) => (
              <Slot
                slot={local.slots?.item}
                context={{
                  index: ctx.index,
                  data: ctx.data,
                  onClick: handleItemClicked,
                  active: isActive(ctx.data!(), ctx.index!),
                  disabled: isDisabled(ctx.data!(), ctx.index!),
                }}
              >
                {ComboBoxItem}
              </Slot>
            ),
          }}
        ></ListBox>
      </div>
    </div>
  );
};

export const ComboBoxItem = <TOption extends {}>(
  p: ParentProps<ComboBoxItemProps<TOption>>
) => {
  const [local, props] = splitProps(p, ["active", "disabled"]);

  // createEffect(() => {
  //   console.log("comboBox item active", p.data?.(), p.active);
  // });

  return <ListBoxItem {...p}></ListBoxItem>;
};

export default Object.assign(ComboBox, {
  Item: ComboBoxItem,
});
